/*
 * @Author: Crazy_Debug yangcheng960813@163.com
 * @Date: 2023-05-17 20:15:06
 * @LastEditTime: 2024-07-12 15:50:42
 * @LastEditors: yayalee yangcheng960813@163.com
 * @Description: 
 * @FilePath: \unfinished-challenges\assets\core_ts\MathUtil.ts
 * 可以输入预定的版权声明、个性签名、空行等
 */

/**数学计算工具类 */
export class MathUtil {

    /**每天小时数 */
    public static readonly HOURS_PER_DAY = 24;
    /**每天分钟数 */
    public static readonly MINUTES_PER_DAY = 24 * 60;
    /**每天秒数 */
    public static readonly SECONDS_PER_DAY = 24 * 60 * 60;
    /**每天毫秒数 */
    public static readonly MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;

    /**
    * 返回value是否在 [min, max] 区间内
    * [表示大于等于 )表示小于 例如[2,3）表示大于等于2，小于3
    * @param min 
    * @param max 
    * @param value
    * @param includeEdge true(默认值): [min, max]; false: (min, max)
    */
    public static inRange(min: number, max: number, value: number, includeEdge: boolean = true): boolean {
        return includeEdge ? value >= min && value <= max : value > min && value < max;
    }

    /**
     * 获取区间[min, max)的整数，传入1个参数则区间为[0, min)
     *  [表示大于等于 )表示小于 例如[2,3）表示大于等于2，小于3
     * @param min 
     * @param max 
     * @returns 
     */
    public static randInt(min: number, max: number = undefined!): number {
        if (max === undefined) {
            max = min;
            min = 0;
        }
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min)) + min;
    }

    /**
     * 获取区间[min, max)的浮点数，传入1个参数则区间为[0, min)
     */
    public static randFloat(min: number, max: number = undefined!): number {
        if (max === undefined) {
            max = min;
            min = 0;
        }
        return Math.random() * (max - min) + min;
    }

    /**
     * 根据权重数组进行随机，返回结果下标
     * @param weightArr 权重数组
     * @returns 随机到的权重数组下标
     */
    public static randWeightIdx(weightArr: number[]) {
        let sum = 0;
        for (let i = 0; i < weightArr.length; i++) {
            sum += weightArr[i];
        }
        let randNum = this.randFloat(0, sum);
        let curValue = 0
        for (let i = 0; i < weightArr.length; i++) {
            curValue += weightArr[i];
            if (randNum < curValue) {
                return i;
            }
        }
        return weightArr.length - 1;
    }

    /**
     * 返回一个概率值(%)的随机结果是否在概率范围内
     * @param rate 概率值(%) [0,n=100]
     */
    public static randRateResult(rate: number, n: number = 100): boolean {
        return this.randFloat(n) < rate;
    }

    /**
     * 随机打乱数组 es6 写法
     * @param arr 
     */
    public static shuffle<T>(arr: Array<T>) {
        let i = arr.length;
        while (--i) {
            let j = Math.floor(Math.random() * i);
            [arr[i], arr[j]] = [arr[j], arr[i]];
        }
        return arr;
    }

    /**
    * Fisher–Yates shuffle 字符串随机乱序
    */
    public static shuffleString(str: string): string {
        let arr: string[] = [];
        for (let i = 0; i < str.length; i++) {
            arr.push(str[i]);
        }
        arr = this.shuffle(arr);
        str = "";
        arr.forEach((v) => {
            str += v;
        });
        return str;
    }

    /**
     * 角度转弧度
     */
    static deg2Rad: number = Math.PI / 180;
    /**
     * 弧度转角度
     */
    static rad2Deg: number = 180 / Math.PI;
    /**
     * 弧度值转换为角度值
     * @param radian 弧度值
     * @returns {number} 返回角度
     */
    public static radian2Angle(radian: number): number {
        return radian * 180 / Math.PI;
    }
    /**
       * 角度值转换为弧度值
       * @param {number} angle 角度
       * @returns {number} 返回弧度值
       */
    public static angle2Radian(angle: number): number {
        return angle * Math.PI / 180;
    }

    /**
     * 获取两点间弧度
     * @param p1X
     * @param p1Y
     * @param p2X
     * @param p2Y
     * @returns {number}
     */
    public static getRadian(p1X: number, p1Y: number, p2X: number, p2Y: number): number {
        var xdis: number = p2X - p1X;
        var ydis: number = p2Y - p1Y;
        return Math.atan2(ydis, xdis);
    }

    /**
     * 获取两点间角度
     * @param p1X
     * @param p1Y
     * @param p2X
     * @param p2Y
     * @returns {number}
     */
    public static getAngle(p1X: number, p1Y: number, p2X: number, p2Y: number): number {
        return this.radian2Angle(this.getRadian(p1X, p1Y, p2X, p2Y));
    }

    // /**
    //  * 反射单位向量
    //  * @param vn 入射单位向量
    //  * @param vi 反射单位向量
    //  * @returns 
    //  */
    // public static getReflect(vn: Vec2, vi: Vec2) {
    //     vn = vn.normalize().clone();
    //     // 入射单位向量
    //     vi = vi.normalize().clone();
    //     // 反射单位向量 vr
    //     return vi.subtract(vn.multiplyScalar(2 * vi.dot(vn)));
    // }

    /**
     * 获取两点间距离
     * @param p1X
     * @param p1Y
     * @param p2X
     * @param p2Y
     * @returns {number} 距离
     */
    public static getDistance(p1X: number, p1Y: number, p2X: number, p2Y: number): number {
        var disX: number = p2X - p1X;
        var disY: number = p2Y - p1Y;
        var disQ: number = disX * disX + disY * disY;
        return Math.sqrt(disQ);
    }

    /**
     * 将值x钳位到范围[a, b]
     * @param a 
     * @param b 
     * @param x 
     * @returns {number} a >= x <= b
     */
    public static clamp(a: number, b: number, x: number): number {
        if (a > b) {
            let t = a;
            a = b;
            b = t;
        }
        if (x < a) return a;
        if (x > b) return b;
        return x;
    }

    /**
     * 判断两个数是否约等于
     * @param {number} a - 第一个数
     * @param {number} b - 第二个数
     * @param {number} epsilon - 容差值（可选）
     * @returns {boolean} - 若两个数约等于则返回 true，否则返回 false
     */
    public static approximatelyEqual(a: number, b: number, epsilon: number = Number.EPSILON): boolean {
        // 比较两个数的绝对差异与 epsilon 的大小
        return Math.abs(a - b) < epsilon;
    }

    public static toFixedStr(value: number, fixCount: number): string {
        return value.toFixed(fixCount).replace(/\.?0*$/, '');
    }

    public static toPercentStr(value: number, fixCount: number = 1): string {
        return this.toFixedStr(value * 100, fixCount) + "%";
    }

}